"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from) {
    for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
        to[j] = from[i];
    return to;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var crossEntropy_1 = __importDefault(require("../../tools/crossEntropy"));
var diffCrossEntropy_1 = __importDefault(require("../../tools/diffCrossEntropy"));
var diffMse_1 = __importDefault(require("../../tools/diffMse"));
var mse_1 = __importDefault(require("../../tools/mse"));
var shuffle_1 = __importDefault(require("../../tools/shuffle"));
var sum_1 = __importDefault(require("../../tools/sum"));
var layer_1 = __importDefault(require("./layer"));
var neuron_1 = __importDefault(require("./neuron"));
var Network = (function () {
    function Network(loss, layers) {
        if (loss === void 0) { loss = 'mse'; }
        this.layers = [];
        this.loss = 'mse';
        this.lossFunctions = {
            'mse': { function: mse_1.default, derivative: diffMse_1.default },
            'crossEntropy': { function: crossEntropy_1.default, derivative: diffCrossEntropy_1.default }
        };
        this.loss = loss;
        layers ? layers.length !== 0 ? this.layers = layers : '' : '';
    }
    Network.prototype.load = function (model) {
        this.layers = [];
        this.loss = model.loss;
        for (var _i = 0, _a = model.configs; _i < _a.length; _i++) {
            var layer = _a[_i];
            var neurons = [];
            for (var _b = 0, _c = layer.neurons; _b < _c.length; _b++) {
                var neuron = _c[_b];
                neurons.push(new neuron_1.default(0, __spreadArray(__spreadArray([], neuron.weigths), [neuron.bias])));
            }
            this.addLayer(layer.shape, neurons.length, layer.activationFunction, neurons);
        }
    };
    Network.prototype.addLayer = function (shape, quant, activationFunction, neurons) {
        if (activationFunction === void 0) { activationFunction = 'none'; }
        this.layers.push(new layer_1.default(shape, quant, activationFunction, neurons));
    };
    Network.prototype.removeLayer = function (index) {
        if (index) {
            this.layers.splice(index, 1);
        }
        else {
            this.layers = [];
        }
    };
    Network.prototype.feedforward = function (inputs) {
        var tempResult = inputs;
        for (var _i = 0, _a = this.layers; _i < _a.length; _i++) {
            var layer = _a[_i];
            tempResult = layer.feedforward(tempResult);
        }
        return tempResult;
    };
    Network.prototype.train = function (trainingData, learningRate, trainingTime) {
        var trainingDataCopy = trainingData.slice(0);
        for (var epoch = 1; epoch <= trainingTime; epoch++) {
            for (var _i = 0, trainingDataCopy_1 = trainingDataCopy; _i < trainingDataCopy_1.length; _i++) {
                var data = trainingDataCopy_1[_i];
                var inputs = data.input;
                var y = data.output;
                var eachLayerOutPut = [this.layers[0].feedforward(inputs)];
                for (var i = 1; i < this.layers.length; i++) {
                    eachLayerOutPut.push(this.layers[i].feedforward(eachLayerOutPut[eachLayerOutPut.length - 1]));
                }
                var partialDerivative = [];
                for (var i = 0; i < y.length; i++) {
                    partialDerivative.push([learningRate * this.lossFunctions[this.loss].derivative(eachLayerOutPut[eachLayerOutPut.length - 1][i], y[i])]);
                }
                for (var i = this.layers.length - 1; i > -1; i--) {
                    partialDerivative = this.layers[i].backward(eachLayerOutPut[i - 1] || inputs, partialDerivative);
                }
            }
            trainingDataCopy = shuffle_1.default(trainingDataCopy);
        }
    };
    Network.prototype.fit = function (trainingData, testingData, learningRate, patient, trainingTime) {
        var trainingDataCopy = trainingData.slice(0);
        var allEpochLoss = [];
        var startPatientCount = false;
        var patientValue = 0;
        for (var epoch = 1; epoch <= trainingTime; epoch++) {
            for (var _i = 0, trainingDataCopy_2 = trainingDataCopy; _i < trainingDataCopy_2.length; _i++) {
                var data = trainingDataCopy_2[_i];
                var inputs = data.input;
                var y = data.output;
                var eachLayerOutPut = [this.layers[0].feedforward(inputs)];
                for (var i = 1; i < this.layers.length; i++) {
                    eachLayerOutPut.push(this.layers[i].feedforward(eachLayerOutPut[eachLayerOutPut.length - 1]));
                }
                var partialDerivative = [];
                for (var i = 0; i < y.length; i++) {
                    partialDerivative.push([learningRate * this.lossFunctions[this.loss].derivative(eachLayerOutPut[eachLayerOutPut.length - 1][i], y[i])]);
                }
                for (var i = this.layers.length - 1; i > -1; i--) {
                    partialDerivative = this.layers[i].backward(eachLayerOutPut[i - 1] || inputs, partialDerivative);
                }
            }
            var allLoss = [];
            for (var _a = 0, testingData_1 = testingData; _a < testingData_1.length; _a++) {
                var data = testingData_1[_a];
                var inputs = data.input;
                var y = data.output;
                var eachLayerOutPut = [this.layers[0].feedforward(inputs)];
                for (var i = 1; i < this.layers.length; i++) {
                    eachLayerOutPut.push(this.layers[i].feedforward(eachLayerOutPut[eachLayerOutPut.length - 1]));
                }
                var temp = [];
                for (var i = 0; i < y.length; i++) {
                    temp.push(Math.abs(this.lossFunctions[this.loss].derivative(eachLayerOutPut[eachLayerOutPut.length - 1][i], y[i])));
                }
                var loss = sum_1.default(temp) / temp.length;
                allLoss.push(loss);
            }
            allEpochLoss.push(sum_1.default(allLoss) / allLoss.length);
            if (allEpochLoss[allEpochLoss.length - 1] === 0) {
                break;
            }
            if (allEpochLoss.length > 1 && allEpochLoss[allEpochLoss.length - 1] > allEpochLoss[allEpochLoss.length - 2]) {
                startPatientCount = true;
            }
            if (startPatientCount === true) {
                patientValue++;
                if (patientValue > patient) {
                    break;
                }
            }
            trainingDataCopy = shuffle_1.default(trainingDataCopy);
        }
        return allEpochLoss;
    };
    Network.prototype.snapshot = function () {
        var configs = [];
        for (var _i = 0, _a = this.layers; _i < _a.length; _i++) {
            var layer = _a[_i];
            configs.push(layer.snapshot());
        }
        return { configs: configs, loss: this.loss };
    };
    return Network;
}());
exports.default = Network;
module.exports = Network;
